package com.ssy.lingxi.workflow.serviceimpl;

import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.workflow.api.model.vo.request.*;
import com.ssy.lingxi.workflow.api.model.vo.response.*;
import com.ssy.lingxi.workflow.entity.ExternalTaskDefinition;
import com.ssy.lingxi.workflow.entity.InternalTaskDefinition;
import com.ssy.lingxi.workflow.model.bo.KeyValueBO;
import com.ssy.lingxi.workflow.model.constant.*;
import com.ssy.lingxi.workflow.service.IExternalTaskDaoService;
import com.ssy.lingxi.workflow.service.IInternationalizeService;
import com.ssy.lingxi.workflow.service.IProcessQueryService;
import com.ssy.lingxi.workflow.service.IProcessRoleService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.task.Task;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *  流程定义查询接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-04-26
 */
@Service
public class ProcessQueryServiceImpl implements IProcessQueryService {
    @Resource
    private IExternalTaskDaoService externalTaskDaoService;

    @Resource
    private IInternationalizeService internationalizeService;

    @Resource
    private IProcessRoleService processRoleService;

    @Resource
    private TaskService taskService;

    /**
     * 查询流程任务定义
     * @param queryVO 接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<ComplexTaskDefVO> findComplexTaskDefinitions(ComplexProcessQueryVO queryVO) {
        ComplexProcessWithRolesQueryVO withRolesQueryVO = new ComplexProcessWithRolesQueryVO();
        withRolesQueryVO.setProcessKey(queryVO.getProcessKey());
        withRolesQueryVO.setTaskId(queryVO.getTaskId());
        withRolesQueryVO.setMemberId(queryVO.getMemberId());
        withRolesQueryVO.setRoleIds(queryVO.getRoleId() != null && queryVO.getRoleId() > 0 ? Stream.of(queryVO.getRoleId()).collect(Collectors.toList()) : new ArrayList<>());
        withRolesQueryVO.setSubRoleIds(queryVO.getSubRoleId() != null && queryVO.getSubRoleId() > 0 ? Stream.of(queryVO.getSubRoleId()).collect(Collectors.toList()) : new ArrayList<>());
        return findComplexTaskDefinitions(withRolesQueryVO);
    }

    /**
     * 查询复杂流程的任务定义
     *
     * @param queryVO 接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<ComplexTaskDefVO> findComplexTaskDefinitions(ComplexProcessWithRolesQueryVO queryVO) {
        List<ExternalTaskDefinition> externalTaskDefinitions = externalTaskDaoService.findByProcessKey(queryVO.getProcessKey()).stream().filter(extTask -> extTask.getTaskType().equals(ProcessTaskTypeEnum.SUB_PROCESS.getCode()) || extTask.getTaskType().equals(ProcessTaskTypeEnum.USER_TASK.getCode())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(externalTaskDefinitions)) {
            return Wrapper.fail(ResponseCode.WF_PROCESS_DEFINITION_DOES_NOT_EXIST);
        }

        if(externalTaskDefinitions.get(0).getProcessType().equals(ProcessTypeEnum.EXTERNAL_PROCESS.getCode())) {
            return Wrapper.fail(ResponseCode.WF_NEED_CALL_EXTERNAL_PROCESS_API);
        }

        if(externalTaskDefinitions.get(0).getProcessType().equals(ProcessTypeEnum.INTERNAL_PROCESS.getCode())) {
            return Wrapper.fail(ResponseCode.WF_NEED_CALL_INTERNAL_PROCESS_API);
        }

        ComplexTaskDefVO defVO = new ComplexTaskDefVO();
        defVO.setProcessName(externalTaskDefinitions.get(0).getProcessName());

        //Step 1: 查找当前流程
        if(StringUtils.hasLength(queryVO.getTaskId())) {
            Task task = taskService.createTaskQuery().taskId(queryVO.getTaskId()).singleResult();
            if(task == null) {
                return Wrapper.fail(ResponseCode.WF_PROCESS_TASK_DOEX_NOT_EXIST_OR_COMPLETED);
            }

            //如果找到外部流程节点，此节点不会是SubProgress，所以不会有关联的内部任务
            ExternalTaskDefinition externalTaskDefinition = externalTaskDefinitions.stream().filter(extTask -> extTask.getTaskName().equals(task.getName())).findFirst().orElse(null);
            if(externalTaskDefinition == null) {
                //从内部任务找
                List<InternalTaskDefinition> internalTaskDefinitions = externalTaskDefinitions.stream().flatMap(extTask -> extTask.getSubTasks().stream()).collect(Collectors.toList());
                InternalTaskDefinition internalTaskDefinition = internalTaskDefinitions.stream().filter(intTask -> intTask.getTaskName().equals(task.getName())).findFirst().orElse(null);
                if(internalTaskDefinition == null) {
                    return Wrapper.fail(ResponseCode.WF_PROCESS_TASK_DOEX_NOT_EXIST_OR_COMPLETED);
                }
                defVO.setCurrentInnerStep(internalTaskDefinition.getTaskStep());
                defVO.setCurrentOuterStep(internalTaskDefinition.getExtTask().getTaskStep());
            } else {
                defVO.setCurrentOuterStep(externalTaskDefinition.getTaskStep());
            }
        }

        List<Long> upperRoleIds = CollectionUtils.isEmpty(queryVO.getRoleIds()) ? new ArrayList<>() : queryVO.getRoleIds().stream().filter(roleId -> roleId > 0).distinct().sorted().collect(Collectors.toList());
        List<Long> subRoleIds = CollectionUtils.isEmpty(queryVO.getSubRoleIds()) ? new ArrayList<>() : queryVO.getSubRoleIds().stream().filter(subRoleId -> subRoleId > 0).distinct().sorted().collect(Collectors.toList());
        String upperRoleName = CollectionUtils.isEmpty(upperRoleIds) ? "" : upperRoleIds.stream().map(roleId -> processRoleService.findExternalTaskRoleName(roleId)).collect(Collectors.joining(","));
        String subRoleName = CollectionUtils.isEmpty(subRoleIds) ? "" : subRoleIds.stream().map(subRoleId -> processRoleService.findExternalTaskRoleName(subRoleId)).collect(Collectors.joining(","));

        Map<String, String> map = new HashMap<>();
        if(queryVO.getMemberId() != null && queryVO.getMemberId() > 0) {
            List<String> urls = externalTaskDefinitions.stream().flatMap(extTask -> extTask.getSubTasks().stream()).filter(intTask -> StringUtils.hasLength(intTask.getUrl())).map(InternalTaskDefinition::getUrl).collect(Collectors.toList());
            map = processRoleService.findInternalTaskRoleName(queryVO.getMemberId(), urls, ModelConstant.WEB_URL_SOURCE);
        }

        //ExternalTaskVO和InternalTaskVO都必须定义equals()方法，才能distinct()去重
        Map<String, String> finalMap = map;
        defVO.setExternalTasks(externalTaskDefinitions.stream().map(extTask -> {
            ExternalTaskVO taskVO = new ExternalTaskVO();
            taskVO.setTaskStep(extTask.getTaskStep());
            taskVO.setTaskName(internationalizeService.findTaskName(extTask.getTaskName()));
            if(extTask.getRoleType().equals(ExternalTaskRoleTypeEnum.PLATFORM.getCode())) {
                taskVO.setMemberRoleName(internationalizeService.findMessage(WorkflowStringEnum.PLATFORM_ROLE_NAME));
            } else if (extTask.getRoleType().equals(ExternalTaskRoleTypeEnum.UPPER_MEMBER.getCode())) {
                taskVO.setMemberRoleName(upperRoleName);
            } else if (extTask.getRoleType().equals(ExternalTaskRoleTypeEnum.SUB_MEMBER.getCode())) {
                taskVO.setMemberRoleName(subRoleName);
            } else {
                taskVO.setMemberRoleName("");
            }

            taskVO.setProperties(extTask.getOther().stream().collect(Collectors.toMap(KeyValueBO::getKey, KeyValueBO::getValue)));

            if(CollectionUtils.isEmpty(extTask.getSubTasks())) {
                taskVO.setSubTasks(new ArrayList<>());
            } else {
                taskVO.setSubTasks(extTask.getSubTasks().stream().map(intTask -> {
                    InternalTaskVO internalTaskVO = new InternalTaskVO();
                    internalTaskVO.setTaskStep(intTask.getTaskStep());
                    internalTaskVO.setTaskName(internationalizeService.findTaskName(intTask.getTaskName()));
                    if(finalMap.isEmpty()) {
                        internalTaskVO.setUserRoleName(internationalizeService.findMessage(WorkflowStringEnum.DEFAULT_INTERNAL_PROCESS_ROLE_NAME));
                    } else {
                        internalTaskVO.setUserRoleName(finalMap.getOrDefault(intTask.getUrl(), internationalizeService.findMessage(WorkflowStringEnum.DEFAULT_INTERNAL_PROCESS_ROLE_NAME)));
                    }
                    internalTaskVO.setProperties(intTask.getOther().stream().collect(Collectors.toMap(KeyValueBO::getKey, KeyValueBO::getValue)));
                    return internalTaskVO;
                }).distinct().sorted(Comparator.comparingInt(InternalTaskVO::getTaskStep)).collect(Collectors.toList()));
            }

            return taskVO;
        }).distinct().sorted(Comparator.comparingInt(ExternalTaskVO::getTaskStep)).collect(Collectors.toList()));

        return Wrapper.success(defVO);
    }


    /**
     * 查询单一的外部流程的定义
     *
     * @param queryVO 接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<SimpleProcessDefVO> findSimpleExternalTaskDefinitions(ExternalProcessQueryVO queryVO) {
        ExternalProcessWithRolesQueryVO withRolesQueryVO = new ExternalProcessWithRolesQueryVO();
        withRolesQueryVO.setProcessKey(queryVO.getProcessKey());
        withRolesQueryVO.setTaskId(queryVO.getTaskId());
        withRolesQueryVO.setRoleIds(queryVO.getRoleId() != null && queryVO.getRoleId() > 0 ? Stream.of(queryVO.getRoleId()).collect(Collectors.toList()) : new ArrayList<>());
        withRolesQueryVO.setSubRoleIds(queryVO.getSubRoleId() != null && queryVO.getSubRoleId() > 0 ? Stream.of(queryVO.getSubRoleId()).collect(Collectors.toList()) : new ArrayList<>());
        return findSimpleExternalTaskDefinitions(withRolesQueryVO);
    }

    /**
     * 查询单一的外部流程的定义
     *
     * @param queryVO 接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<SimpleProcessDefVO> findSimpleExternalTaskDefinitions(ExternalProcessWithRolesQueryVO queryVO) {
        List<ExternalTaskDefinition> externalTaskDefinitions = externalTaskDaoService.findByProcessKey(queryVO.getProcessKey()).stream().filter(task -> task.getTaskType().equals(ProcessTaskTypeEnum.SUB_PROCESS.getCode()) || task.getTaskType().equals(ProcessTaskTypeEnum.USER_TASK.getCode())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(externalTaskDefinitions)) {
            return Wrapper.fail(ResponseCode.WF_PROCESS_DEFINITION_DOES_NOT_EXIST);
        }

        if(externalTaskDefinitions.get(0).getProcessType().equals(ProcessTypeEnum.COMPLEX_PROCESS.getCode())) {
            return Wrapper.fail(ResponseCode.WF_NEED_CALL_COMPLEX_PROCESS_API);
        }

        if(externalTaskDefinitions.get(0).getProcessType().equals(ProcessTypeEnum.INTERNAL_PROCESS.getCode())) {
            return Wrapper.fail(ResponseCode.WF_NEED_CALL_INTERNAL_PROCESS_API);
        }

        Integer currentStep = 0;
        if(StringUtils.hasLength(queryVO.getTaskId())) {
            Task task = taskService.createTaskQuery().taskId(queryVO.getTaskId()).singleResult();
            if(task != null) {
                ExternalTaskDefinition externalTaskDefinition = externalTaskDefinitions.stream().filter(extTask -> extTask.getTaskName().equals(task.getName())).findFirst().orElse(null);
                if(externalTaskDefinition != null) {
                    currentStep = externalTaskDefinition.getTaskStep();
                }
            }
        }

        List<Long> upperRoleIds = CollectionUtils.isEmpty(queryVO.getRoleIds()) ? new ArrayList<>() : queryVO.getRoleIds().stream().filter(roleId -> roleId > 0).distinct().sorted().collect(Collectors.toList());
        List<Long> subRoleIds = CollectionUtils.isEmpty(queryVO.getSubRoleIds()) ? new ArrayList<>() : queryVO.getSubRoleIds().stream().filter(subRoleId -> subRoleId > 0).distinct().sorted().collect(Collectors.toList());
        String upperRoleName = CollectionUtils.isEmpty(upperRoleIds) ? "" : upperRoleIds.stream().map(roleId -> processRoleService.findExternalTaskRoleName(roleId)).collect(Collectors.joining(","));
        String subRoleName = CollectionUtils.isEmpty(subRoleIds) ? "" : subRoleIds.stream().map(subRoleId -> processRoleService.findExternalTaskRoleName(subRoleId)).collect(Collectors.joining(","));

        //SimpleTaskVO必须定义equals()方法，才能使用distinct()去重
        SimpleProcessDefVO defVO = new SimpleProcessDefVO();
        defVO.setCurrentStep(currentStep);
        defVO.setProcessName(externalTaskDefinitions.get(0).getProcessName());
        defVO.setTasks(externalTaskDefinitions.stream().map(task ->{
            SimpleTaskVO taskVO = new SimpleTaskVO();
            taskVO.setTaskStep(task.getTaskStep());
            taskVO.setTaskName(internationalizeService.findTaskName(task.getTaskName()));
            if(task.getRoleType().equals(ExternalTaskRoleTypeEnum.PLATFORM.getCode())) {
                taskVO.setRoleName(internationalizeService.findMessage(WorkflowStringEnum.PLATFORM_ROLE_NAME));
            } else if (task.getRoleType().equals(ExternalTaskRoleTypeEnum.UPPER_MEMBER.getCode())) {
                taskVO.setRoleName(upperRoleName);
            } else if (task.getRoleType().equals(ExternalTaskRoleTypeEnum.SUB_MEMBER.getCode())) {
                taskVO.setRoleName(subRoleName);
            } else {
                taskVO.setRoleName("");
            }

            taskVO.setProperties(task.getOther().stream().collect(Collectors.toMap(KeyValueBO::getKey, KeyValueBO::getValue)));
            return taskVO;
        }).distinct().sorted(Comparator.comparingInt(SimpleTaskVO::getTaskStep)).collect(Collectors.toList()));

        return Wrapper.success(defVO);
    }

    /**
     * 查询单一的内部流程的定义
     *
     * @param queryVO 接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<SimpleProcessDefVO> findSimpleInternalTaskDefinitions(InternalProcessQueryVO queryVO) {
        //由于外部流程有缓存，所以从外部流程的虚拟节点查
        List<ExternalTaskDefinition> externalTaskDefinitions = externalTaskDaoService.findByProcessKey(queryVO.getProcessKey());
        if(CollectionUtils.isEmpty(externalTaskDefinitions)) {
            return Wrapper.fail(ResponseCode.WF_PROCESS_DEFINITION_DOES_NOT_EXIST);
        }

        if(externalTaskDefinitions.get(0).getProcessType().equals(ProcessTypeEnum.COMPLEX_PROCESS.getCode())) {
            return Wrapper.fail(ResponseCode.WF_NEED_CALL_COMPLEX_PROCESS_API);
        }

        if(externalTaskDefinitions.get(0).getProcessType().equals(ProcessTypeEnum.EXTERNAL_PROCESS.getCode())) {
            return Wrapper.fail(ResponseCode.WF_NEED_CALL_EXTERNAL_PROCESS_API);
        }


        ExternalTaskDefinition externalTaskDefinition = externalTaskDefinitions.stream().filter(extTask -> extTask.getTaskType().equals(ProcessTaskTypeEnum.VIRTUAL_TASK.getCode())).findFirst().orElse(null);
        if(externalTaskDefinition == null) {
            return Wrapper.fail(ResponseCode.SERVICE_ERROR);
        }

        List<InternalTaskDefinition> internalTaskDefinitions = new ArrayList<>(externalTaskDefinition.getSubTasks());
        if(CollectionUtils.isEmpty(internalTaskDefinitions)) {
            return Wrapper.success(new SimpleProcessDefVO());
        }

        //查找当前步骤
        Integer currentStep = 0;
        if(StringUtils.hasLength(queryVO.getTaskId())) {
            Task task = taskService.createTaskQuery().taskId(queryVO.getTaskId()).singleResult();
            if(task != null) {
                InternalTaskDefinition internalTaskDefinition = internalTaskDefinitions.stream().filter(intTask -> intTask.getTaskName().equals(task.getName())).findFirst().orElse(null);
                if(internalTaskDefinition != null) {
                    currentStep = internalTaskDefinition.getTaskStep();
                }
            }
        }

        //因为内部任务会有重复的taskStep，taskName，为减少数据库查询，所以先批量查询一下关联的角色
        Map<String, String> map = new HashMap<>();
        if(queryVO.getMemberId() != null && queryVO.getMemberId() > 0) {
            List<String> urls = internalTaskDefinitions.stream().filter(intTask -> StringUtils.hasLength(intTask.getUrl())).map(InternalTaskDefinition::getUrl).collect(Collectors.toList());
            map = processRoleService.findInternalTaskRoleName(queryVO.getMemberId(), urls, ModelConstant.WEB_URL_SOURCE);
        }

        SimpleProcessDefVO defVO = new SimpleProcessDefVO();
        defVO.setProcessName(externalTaskDefinition.getProcessName());
        defVO.setCurrentStep(currentStep);

        //SimpleTaskVO必须定义equals()方法，才能使用distinct()去重
        Map<String, String> finalMap = map;
        defVO.setTasks(internalTaskDefinitions.stream().map(internalTaskDefinition -> {
            SimpleTaskVO taskVO = new SimpleTaskVO();
            taskVO.setTaskStep(internalTaskDefinition.getTaskStep());
            taskVO.setTaskName(internationalizeService.findTaskName(internalTaskDefinition.getTaskName()));
            if(finalMap.isEmpty()) {
                taskVO.setRoleName(internationalizeService.findMessage(WorkflowStringEnum.DEFAULT_INTERNAL_PROCESS_ROLE_NAME));
            } else {
                taskVO.setRoleName(finalMap.getOrDefault(internalTaskDefinition.getUrl(), internationalizeService.findMessage(WorkflowStringEnum.DEFAULT_INTERNAL_PROCESS_ROLE_NAME)));
            }
            taskVO.setProperties(internalTaskDefinition.getOther().stream().collect(Collectors.toMap(KeyValueBO::getKey, KeyValueBO::getValue)));
            return taskVO;
        }).distinct().sorted(Comparator.comparingInt(SimpleTaskVO::getTaskStep)).collect(Collectors.toList()));

        return Wrapper.success(defVO);
    }

}
